package top.chukongxiang.monitorservice.tools.rtp;

import lombok.RequiredArgsConstructor;

/**
 * @author 楚孔响
 * @version 1.0
 * @date 2023/9/13 16:59
 */
@RequiredArgsConstructor
public class RtpWrapper {

    private final byte[] bytes;
    private String version;
    private Byte padding;
    private Byte extension;
    private Byte csrcCount;
    private Byte marker;

    /**
     * 载荷类型
     * 执行标准：RFC3551
     */
    private Byte payloadType;
    private Short sequenceNumber;
    private Integer timestamp;
    private Integer ssrc;
    private Integer csrc;
    private byte[] payload;
    private StreamWrapper streamWrapper;

    public String getVersion() {
        if (this.version != null) {
            return this.version;
        }
        int mainVersion = bytes[0] >> 7 & 0x1;
        int subVersion = bytes[0] >> 6 & 0x1;
        this.version = mainVersion + "." + subVersion;
        return this.version;
    }

    /**
     * Padding标记，取值0-1，0表示Payload后面没有填充，1代表Payload后跟有1个或最多8个字节的填充，如果有填充，RTP包最后一个字节是填充计数器，表示包含自身在内的填充的字节数
     * @return
     */
    public byte getPadding() {
        if (this.padding != null) {
            return this.padding;
        }
        this.padding = (byte) (bytes[0] >> 5 & 0x1);
        return this.padding;
    }

    /**
     * 扩展标志，占1位，如果X=1，则在RTP报头后跟有一个扩展报头
     * @return
     */
    public byte getExtension() {
        if (this.extension != null) {
            return this.extension;
        }
        this.extension = (byte) (bytes[0] >> 4 & 0x1);
        return this.extension;
    }

    /**
     * CSRC计数器，占4位，指示CSRC标识符的个数
     * @return
     */
    public byte getCSRCCount() {
        if (this.csrcCount != null) {
            return this.csrcCount;
        }
        this.csrcCount = (byte) (bytes[0] & 0xf);
        return this.csrcCount;
    }

    /**
     * 标记位，占1位，由具体的profile定义其含义,
     * 不同的有效载荷有不同的含义，对于视频，标记一帧的结束；对于音频，标记会话的开始
     * @return
     */
    public byte getMarker() {
        if (this.marker != null) {
            return this.marker;
        }
        this.marker = (byte) (bytes[1] >> 7 & 0x1);
        return this.marker;
    }

    /**
     * 有效载荷类型，占7位，用于说明RTP报文中有效载荷的类型，如GSM音频、JPEM图像等
     * @return
     */
    public byte getPayloadType() {
        if (this.payloadType != null) {
            return this.payloadType;
        }
        this.payloadType = (byte) (bytes[1] & 0x7f);
        return this.payloadType;
    }

    /**
     * 序列号，占16位，用于标识发送者所发送的RTP报文的序列号，每发送一个报文，序列号增1。接收者通过序列号来检测报文丢失情况，重新排序报文，恢复数据
     * @return
     */
    public short getSequenceNumber() {
        if (this.sequenceNumber != null) {
            return this.sequenceNumber;
        }
        this.sequenceNumber = (short) ((bytes[2] & 0xff) << 8 | (bytes[3] & 0xff));
        return this.sequenceNumber;
    }

    /**
     * 时间戳，占32位，时戳反映了该RTP报文的第一个八位组的采样时刻。接收者使用时戳来计算延迟和延迟抖动，并进行同步控制
     * @return
     */
    public int getTimestamp() {
        if (this.timestamp != null) {
            return this.timestamp;
        }
        this.timestamp = (bytes[4] & 0xff) << 24 | (bytes[5] & 0xff) << 16 | (bytes[6] & 0xff) << 8 | (bytes[7] & 0xff);
        return this.timestamp;
    }

    /**
     * 同步信源标识符，占32位，用于标识同步信源。该标识符是随机选择的，对弈同一个视频源，每个会话都有自己独立的同步信源
     * @return
     */
    public int getSsrc() {
        if (this.ssrc != null) {
            return this.ssrc;
        }
        this.ssrc = (bytes[8] & 0xff) << 24 | (bytes[9] & 0xff) << 16 | (bytes[10] & 0xff) << 8 | (bytes[11] & 0xff);
        return this.ssrc;
    }

    /**
     * 特约信源标识符，每个CSRC信源占32位，可以有0-15个，具体数量由CC决定，每个CSRC标识了包含在该RTP报文有效载荷中的所有特约信源
     * @return
     */
    public Integer getCsrc() {
        if (getCSRCCount() <= 0) {
            return null;
        }
        if (this.csrc != null) {
            return this.csrc;
        }
        this.csrc = (bytes[12] & 0xff) << 24 | (bytes[13] & 0xff) << 16 | (bytes[14] & 0xff) << 8 | (bytes[15] & 0xff);
        return this.csrc;
    }

    public StreamWrapper getPayloadStream(RtpPayload rtpPayload) {
        if (this.streamWrapper != null) {
            return this.streamWrapper;
        }
        switch (rtpPayload) {
            case PS: {
                this.streamWrapper = new PsWrapper(getPayload());
                break;
            }
            default:
                return null;
        }
        return this.streamWrapper;
    }

    public byte[] getPayload() {
        if (this.payload != null) {
            return this.payload;
        }
        int offset = 12 + getCSRCCount();
        this.payload = new byte[bytes.length - offset];
        System.arraycopy(bytes, offset, this.payload, 0, this.payload.length);
        return this.payload;
    }

}
